package j.framework.base.service.impl;

import j.data.ActionResult;
import j.data.PagedData;
import j.framework.base.service.SystemService;
import j.framework.core.*;
import j.framework.core.entity.*;
import j.framework.core.entity.EntityTrigger;
import j.framework.query.core.*;
import j.util.*;
import j.util.jdbc.*;
import j.util.pkgenerator.PG;
import j.wcs.cache.dict.DictItem;
import j.wcs.core.WcsContext;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**\系统级的一些方法，主要为集成的框架服务
 * User: jason
 * Date: 2015/3/31 13:06
 */
public class SystemServiceImpl implements SystemService {
    private static final Logger log = LoggerFactory.getLogger(SystemServiceImpl.class);
    private @Autowired
    JdbcTemplate jdbcTemplate;
    //根据查询key或查询ID获取自定义查询元数据
    @Override
    public QueryMetadata getQueryMetadata(String queryKey) {
        final QueryMetadata qmd = jdbcTemplate.queryForDomain("SELECT * FROM sys_query WHERE query_id=? or query_key=?",
                queryMetadataRowMapper, queryKey, queryKey);
        //加载所有的查询字段，并附加到所对应的查询上，根据queryId
        List<QueryFieldMetadata> fields = jdbcTemplate.queryForList("select * from sys_query_fields where query_id=? ORDER BY display_order",
                queryFieldMetadataRowMapper,qmd.getQueryId());
        for(QueryFieldMetadata f : fields){
            qmd.putFieldMetadata(f);
        }
        //加载footer
        jdbcTemplate.queryForList("SELECT f.*,qf.query_id FROM sys_query_field_footers f INNER JOIN sys_query_fields qf ON f.query_field_id=qf.query_field_id where qf.query_id=?", new ResultSetHandler() {
            @Override
            public void doResultSet(ResultSet rs) throws SQLException {
                QueryFieldFooter footer = new QueryFieldFooter();
                footer.setQueryFieldId(rs.getString("query_field_id"));
                footer.setFunctionType(rs.getString("function_type"));
                footer.setFormatOptions(rs.getString("format_options"));
                footer.setRowIndex(rs.getInt("row_index"));
                qmd.putFooter(footer);
            }
        },qmd.getQueryId());
        //加载行操作
        jdbcTemplate.queryForList("SELECT * FROM sys_query_actions where query_id=? ORDER BY display_order", new ResultSetHandler() {
            @Override
            public void doResultSet(ResultSet rs) throws SQLException {
                QueryAction action = new QueryAction();
                action.setActionId(rs.getString("query_action_id"));
                action.setQueryId(rs.getString("query_id"));
                action.setActionId(rs.getString("action_id"));
                action.setActionName(rs.getString("name"));
                action.setOnClick(rs.getString("on_click"));
                action.setGenerateMethodName(rs.getString("generate_event_method_name"));
                qmd.putAction(action);
            }
        }, qmd.getQueryId());
        return qmd;
    }

    @Override
    public WebRequestMetadata getWebRequestMetadata(String requestId) {
        return jdbcTemplate.queryForDomain("SELECT * FROM sys_web_request WHERE web_request_id=?", requestRowMapper,requestId);
    }

    private class QueryBooleanFormatOptions{
        private String trueToDisplayName;
        private String falseToDisplayName;

        public QueryBooleanFormatOptions(String options){
            if(StringUtils.isNullOrEmpty(options)){
                trueToDisplayName = "是";
                falseToDisplayName = "否";
            }else {
                Map<String, Object> map = JsonUtils.toMap(options);
                trueToDisplayName = (String) map.get("Y");
                if (StringUtils.isNullOrEmpty(trueToDisplayName)) {
                    trueToDisplayName = "是";
                }
                falseToDisplayName = (String) map.get("N");
                if (StringUtils.isNullOrEmpty(falseToDisplayName)) {
                    falseToDisplayName = "否";
                }
            }
        }

        public String getTrueToDisplayName() {
            return trueToDisplayName;
        }
        public String getFalseToDisplayName() {
            return falseToDisplayName;
        }
    }

    @Override
    public QueryResult queryData(QueryContext queryContext) {
        return queryData(queryContext, false);
    }

    @Override
    public QueryResult queryData(QueryContext queryContext, boolean noPaging) {
        QueryResult qr = new QueryResult();
        QueryJdbcTemplate qjt = new QueryJdbcTemplate(jdbcTemplate.getDataSource());
        try{
            queryContext.setJdbcTemplate(qjt);
            String  _sql;
            List<Object> args;
            Object bean = null;
            if(StringUtils.isNotEmpty(queryContext.qmd.getBeanClassName())){
                bean = JUtils.getBeanByClassName(queryContext.qmd.getBeanClassName());
                if(bean==null){
                    throw new ServiceException(String.format("bean[%s]的bean不存在，请与系统开发人员联系！",
                            queryContext.qmd.getBeanClassName()));
                }
            }
            String orderBy = queryContext.qmd.buildQueryOrder(queryContext.params.get("sort"),queryContext.params.get("order"));
            if(queryContext.qmd.isProcedure()){
                _sql = queryContext.qmd.getQuerySql();
                Map<String,Object> callParams = New.map();
                //将查询条件加到参数中
                callParams.put("orderBy",orderBy);
                callParams.putAll(queryContext.params);
                TwoTuple<String,List<Object>> tuple = JdbcUtils.parseNamedSql(_sql, callParams);
                _sql = tuple.first;
                args = tuple.second;
            }else{
                _sql = queryContext.qmd.getQuerySql();
                //构造where部分
                if(queryContext.qmd.isNamedSql()){
                    Map<String,Object> params = New.map();
                    params.putAll(queryContext.params);
                    if(bean!=null && StringUtils.isNotEmpty(queryContext.qmd.getBuildWhereMethodName())) {
                        try{
                            Method m = bean.getClass().getDeclaredMethod(queryContext.qmd.getBuildWhereMethodName(),
                                    LoginUser.class, JdbcTemplate.class, Map.class);
                            String where = (String)m.invoke(bean,queryContext.user,queryContext.getJdbcTemplate(),params);
                            if(StringUtils.isNotEmpty(where)){
                                _sql+=" "+where;
                            }
                        }catch (Exception e){
                            throw new ServiceException("查询失败，异常："+e.getMessage());
                        }
                    }
                    TwoTuple<String,List<Object>> tuple = JdbcUtils.parseNamedSql(_sql, params);
                    _sql = tuple.first;
                    args = tuple.second;
                }else{
                    args = New.list();
                    if(bean!=null) {
                        try{
                            Method m = bean.getClass().getDeclaredMethod(queryContext.qmd.getBuildWhereMethodName(),
                                    LoginUser.class, JdbcTemplate.class, Map.class,List.class);
                            String where = (String)m.invoke(bean,queryContext.user,queryContext.getJdbcTemplate(),
                                    queryContext.params,args);
                            if(StringUtils.isNotEmpty(where)){
                                _sql+=" "+where;
                            }
                        }catch (Exception e){
                            throw new ServiceException("查询失败，异常："+e.getMessage());
                        }
                    }
                }
                if(StringUtils.isNotEmpty(orderBy)){
                    _sql += orderBy;
                }
            }
            List<Map<String,Object>> listMap;
            if(queryContext.qmd.isPagingSupported()){
                int pageIndex = ConvertUtils.toInt(queryContext.params.get("page"), 1);
                int pageSize = ConvertUtils.toInt(queryContext.params.get("rows"), noPaging?Integer.MAX_VALUE:20);
                PagedData<List<Map<String,Object>>> pd = qjt.queryForPaging(_sql,pageIndex,pageSize,args.toArray());
                qr.setTotalRecords(pd.getTotal());
                listMap = pd.getDataItems();
            }else{
                listMap = qjt.queryForListMap(_sql, args.toArray());
                qr.setTotalRecords(listMap.size());
            }
            //处理结果集
            List<Map<String,Object>> newListMap = New.list();
            Map<String,Object> map;
            QueryFieldMetadata qfmd;
            List<QueryAction> rowActions = queryContext.qmd.getActions();
            boolean hasActions = rowActions!=null && rowActions.size()>0;
            Object value;
            Boolean booValue;
            DictItem dictionaryItem;
            Map<String,QueryBooleanFormatOptions> booleanFormatOptionsMap = New.map();
            QueryBooleanFormatOptions bfo;
            for(Map<String,Object> m : listMap){
                map = New.map();

                for(Map.Entry<String,Object> entry : m.entrySet()){
                    qfmd = queryContext.qmd.getFieldMetadataByFieldName(entry.getKey());
                    if(qfmd == null){
                        continue;
                    }
                    value = entry.getValue();
                    if(value != null){
                        //格式化
                        if(qfmd.getFormatType().equals(FormatType.NONE)){
                            ;
                        }else if(qfmd.getFormatType().equals(FormatType.BOOLEAN)){
                            booValue = ConvertUtils.toBoolean(value.toString());
                            bfo = booleanFormatOptionsMap.get(qfmd.getQueryFieldId());
                            if(bfo == null){
                                bfo = new QueryBooleanFormatOptions(qfmd.getFormatOptions());
                                booleanFormatOptionsMap.put(qfmd.getQueryFieldId(),bfo);
                            }
                            if(booValue){
                                value = bfo.getTrueToDisplayName();
                            }else{
                                value = bfo.getFalseToDisplayName();
                            }
                        }else if(qfmd.getFormatType().equals(FormatType.DICTIONARY)){
                            dictionaryItem = WcsContext.getContext().getDictItem(qfmd.getFormatOptions(), value.toString());
                            if(dictionaryItem!=null){
                                value = dictionaryItem.getName();
                            }
                        }else if(qfmd.getFormatType().equals(FormatType.USER)){
                            //todo 格式化为增加人

                        }
                    }
                    if(qfmd.isUsedFieldNameAsKey()){
                        map.put(entry.getKey(),value);
                    }else{
                        map.put(qfmd.getQueryFieldId(),value);
                    }
                }
                if(queryContext.qmd.isTreeTable()){
                    map.put("_parentId",m.get(queryContext.qmd.getTreeTableParentFieldName()));
                }
                List<DataColumn> primaryKeys = queryContext.qmd.getPrimaryKeys();
                String desId = "";
                if(null!=primaryKeys && primaryKeys.size()>0){
                    for(DataColumn c : primaryKeys){
                        desId+=m.get(c.getColumnName())+"_";
                    }
                    desId = desId.substring(0,desId.length()-1);
                    desId = DES.encrypt(desId);
                    map.put("desId",desId);
                    m.put("desId",desId);
                }
                if(hasActions){
                    map.put("actions",buildRowActions(queryContext.user,
                            queryContext.getJdbcTemplate(),bean,rowActions,m));
                }
                newListMap.add(map);
            }
            qr.setList(newListMap);
        }finally {
            qjt.close();
        }
        return qr;
    }

    private static String buildRowActions(LoginUser user,JdbcTemplate jdbcTemplate, Object bean,List<QueryAction> actions,Map<String,Object> rowData){
        StringBuilder s = new StringBuilder();
        Method m;
        try {
            Map<String,Object> map = New.map();
            map.putAll(rowData);
            for (QueryAction action : actions) {
                if(StringUtils.isNotEmpty(action.getActionId())){
                    //必须具备此操作权限，才会出现此按钮
                    /*if(!user.hasAction(null, action.getActionId())){
                        continue;
                    }*/
                }
                if(action.getCustomParametersMap()!=null){
                    map.putAll(action.getCustomParametersMap());
                }
                if (StringUtils.isNotEmpty(action.getGenerateMethodName()) && bean != null) {
                    m = bean.getClass().getDeclaredMethod(action.getGenerateMethodName(),
                            QueryAction.class, LoginUser.class, JdbcTemplate.class,Map.class);
                    s.append(m.invoke(bean,action,user,jdbcTemplate,map)).append(" ");
                }else {
                    s.append("<a href=\"javascript:void(0);\" onclick=\"");
                    s.append(StringUtils.format(action.getOnClick(), map));
                    s.append("\">").append(action.getActionName()).append("</a> ");
                }
                //再将action的自定义参数移除，以不至于为后续的操作造成困扰
                if(action.getCustomParametersMap()!=null){
                    for(String key:action.getCustomParametersMap().keySet()){
                        map.remove(key);
                    }
                }
            }
        }catch (Exception e){
            throw new ServiceException("构造查询行操作时出错，异常："+e.getMessage());
        }
        s.deleteCharAt(s.length()-1);
        return s.toString();
    }

    @Override
    public Entity getEntity(String entityId) {
        QueryJdbcTemplate qjt = new QueryJdbcTemplate(jdbcTemplate.getDataSource());
        try {
            final Entity entity = qjt.queryForDomain("SELECT * FROM sys_entity WHERE entity_id=?", entityRowMapper, entityId);
            //加载属性
            List<EntityProperty> properties = qjt.queryForList("SELECT * FROM sys_entity_properties WHERE entity_id=?",
                    entityPropertyRowMapper, entityId);
            for (EntityProperty p : properties) {
                loadEntityProperty(entity, p);
            }
            //加载主键与复合键
            final Map<String, EntityKey> keys = New.map();
            qjt.queryForList("SELECT k.entity_key_id,k.entity_id,k.name,k.key_type,p.table_field_name FROM sys_entity_key_property kp INNER JOIN sys_entity_key k ON kp.entity_key_id=k.entity_key_id INNER JOIN sys_entity_properties p ON kp.entity_property_id=p.entity_property_id WHERE k.entity_id=?",
                    new ResultSetHandler() {
                        @Override
                        public void doResultSet(ResultSet rs) throws SQLException {
                            if (rs.getString("key_type").equalsIgnoreCase("P")) {
                                entity.primaryKeys.add(rs.getString("table_field_name"));
                            } else {
                                //这是复合键
                                EntityKey key = keys.get(rs.getString("entity_key_id"));
                                if (key != null) {
                                    key.tableFieldNames.add(rs.getString("table_field_name"));
                                } else {
                                    key = new EntityKey();
                                    key.setKeyName(rs.getString("name"));
                                    key.tableFieldNames.add(rs.getString("table_field_name"));
                                    keys.put(rs.getString("entity_key_id"), key);
                                }
                            }
                        }
                    }, entityId);
            if (keys.size() > 0) {
                List<EntityKey> uniqueKeys = New.list(keys.size());
                uniqueKeys.addAll(keys.values());
                entity.setUniqueKeys(uniqueKeys);
            }
            //加载实体触发器
            List<EntityInvokeTrigger> triggers = qjt.queryForList("select * from sys_entity_trigger where entity_id=?",
                    entityInvokeTriggerRowMaper,entityId);
            if(triggers!=null && triggers.size()>0){
                for(EntityInvokeTrigger t : triggers){
                    entity.addInvokeTrigger(t);
                }
            }
            return entity;
        }finally {
            qjt.close();
        }
    }

    @Override
    public Map<String, Object> findEntity(Entity e, Map<String, String> parameters) {
        if(e.primaryKeys.size()==0){
            throw new ServiceException(String.format("实体[%s]配置有误，没有配置主键，请与开发人员联系！",e.getName()));
        }
        List<Object> argsList = New.list();
        TwoTuple<String,List<String>> tt = e.getFindByPkSql();
        EntityProperty p;
        String pValue;
        for(String key : tt.second){
            p = e.properties.get(key);
            if(p == null){
                throw new ServiceException(String.format("实体[%s]没有配置属性[%s]！",e.getName(),key));
            }
            pValue = parameters.get(key);
            if(p.isDesEncrypted() && StringUtils.isNotEmpty(pValue)){
                pValue = DES.decrypt(pValue);
                if(StringUtils.isNullOrEmpty(pValue)){
                    //解密错误，抛出异常
                    throw new ServiceException(String.format("查找实体[%s]时参数错误，请检查参数[%s]的值！",
                            e.getName(),p.getLabelName()));
                }
            }
            if(StringUtils.isNullOrEmpty(pValue)){
                //在查找实体时，若有其中一个参数为空，则返回空集合数据
                return New.map();
            }
            argsList.add(pValue);
        }
        Object[] args = argsList.toArray();
        return jdbcTemplate.queryForMap(tt.first, args);
    }

    @Override
    public Map<String,Object> insertEntity(LoginUser user, Entity e, Map<String, String> parameters, ActionResult ar) {
        if(e.primaryKeys.size()==0){
            throw new ServiceException(String.format("实体[%s]配置有误，没有配置主键，请与开发人员联系！",e.getName()));
        }
        //将参数按照实体属性的类型进行转换
        Map<String,Object> mapData = processParameters(parameters, e, ar);
        if(ar.hasErrors()){
            return mapData;
        }
        //将增加人及增加时间放到参数中
        mapData.put(Entity.ADDED_USER_ID_FIELD_NAME,user.getUserId());
        mapData.put(Entity.ADDED_TIME_FIELD_NAME,new Date());
        //增加序列
        boolean needCheckPrimaryKeyDouble = false;//用来记录是否需要判断主键重复的问题，当主键没有指定序列，且前台又传过来了主键值，在此情况下需要判断主键重复的问题
        if(e.primaryKeys.size()==1){
            if(StringUtils.isNotEmpty(e.getSequenceKey())){
                mapData.put(e.getPrimaryKeys(), PG.getNext(e.getSequenceKey()));
            }else{
                //检查是否通过表单传递主键值
                String pkId = parameters.get(e.getPrimaryKeys());
                if(StringUtils.isNullOrEmpty(pkId)){
                    ar.add("err","此实体没有配置序列，也没有在表单中设置主键，请配置序列或在表单中增加主键值！");
                    return mapData;
                }
                //没有为主键指定序列
                needCheckPrimaryKeyDouble = true;
            }
        }
        //数据类型难过，验证必填项
        EntityProperty p;
        Object pValue;
        for(String key : e.insertableFields){
            p = e.properties.get(key);
            if(p == null){
                ar.add("err",String.format("实体属性[%s]没有定义，请与开发人员联系！",key));
                return mapData;
            }
            pValue = mapData.get(key);
            //验证必填项
            if(p.isRequired() && pValue == null && null == p.getDefaultValue() && p.isInsertable()){
                //这是必填项，但没有此值
                ar.add(p.getFieldName(),String.format("参数[%s]是必填项，请输入正确的数据！", p.getLabelName()));
            }
            if(null == pValue && null!=p.getDefaultValue()){
                //此字段设有默认值
                mapData.put(key,p.getDefaultValue());
            }
        }
        if(ar.hasErrors()){
            return mapData;
        }
        List<Object> argsList = New.list();
        TransactionalJdbcTemplate tjt = new TransactionalJdbcTemplate(jdbcTemplate.getDataSource(),true);
        try{
            //先根据实体的唯一键来判断是否重复
            int cnt;
            if(needCheckPrimaryKeyDouble){
                String key = e.getPrimaryKeys();
                cnt = tjt.queryForInt(String.format("SELECT COUNT(*) FROM %s WHERE %s=?",
                        e.getTableName(), key),parameters.get(key));
                if(cnt>0){
                    p = e.properties.get(key);
                    ar.add("dbl",String.format("实体[%s]已存在，判断依据主键[%s]，请确认！",e.getName(),
                            p.getLabelName()));
                    return mapData;
                }
            }
            List<EntityKey> uniqueKeys = e.getUniqueKeys();
            TwoTuple<String,List<String>> tt;
            if(uniqueKeys!= null && uniqueKeys.size()>0){
                //for update lock
                tjt.queryForObject("SET LOCK_TIMEOUT "+FApplication.SQL_LOCK_TIMEOUT+";SELECT COUNT(*) FROM sys_entity (UPDLOCK) WHERE entity_id=?", e.getEntityId());
                Object argsValue;
                boolean isNeedToCheck = true;
                for(EntityKey key : uniqueKeys){
                    argsList.clear();
                    tt = key.getCheckDoubleForInsertedSql(e);
                    for(String fieldName : tt.second){
                        argsValue = mapData.get(fieldName);
                        if(argsValue==null){
                            isNeedToCheck = false;
                            break;
                        }
                        argsList.add(argsValue);
                    }
                    if(!isNeedToCheck){
                        continue;
                    }
                    cnt = tjt.queryForInt(tt.first,argsList.toArray());
                    if(cnt>0){
                        ar.add("dbl",String.format("实体[%s]已存在，判断依据[%s]，请确认！",e.getName(),
                                key.getKeyName()));
                        return mapData;
                    }
                }
            }
            //增加前触发器
            EntityTrigger trigger = EntityTriggerManager.getTrigger(e.getTriggerClassName());
            if(null != trigger){
                //定义有触发器，调用之
                boolean ret = trigger.beforeInsert(user, e, mapData, tjt, ar);
                if(!ret || ar.hasErrors()){
                    //触发器中增加了错误消息，不再往后执行
                    return mapData;
                }
            }
            //增加前自定义的触发器
            List<EntityInvokeTrigger> triggers = e.getInvokeTriggers(EntityInvokeTrigger.WHEN_INVOKE_AT_BEFORE_INSERT);
            if(triggers!=null && triggers.size()>0){
                for(EntityInvokeTrigger t : triggers){
                    if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_SQL)){
                        //以sql方式执行
                        tjt.executeByNamedSql(t.getNamedSql(),mapData);
                    }else if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_BEAN)){
                        //以bean方式执行
                        EntityTriggerManager.invokeBeanTriggerMethod(EntityInvokeTrigger.WHEN_INVOKE_AT_BEFORE_INSERT, t, user, tjt, mapData, ar);
                        if(ar.hasErrors()){
                            return mapData;
                        }
                    }
                }
            }
            //没有重复，开始增加
            tt = e.getInsertedSql();
            argsList.clear();
            for (String key : tt.second){
                argsList.add(mapData.get(key));
            }
            tjt.execute(tt.first,argsList.toArray());
            //增加后触发器
            if(null != trigger){
                //定义有触发器，调用之
                trigger.afterInsert(user, e, mapData, tjt, ar);
                if(ar.hasErrors()){
                    //触发器中增加了错误消息，不再往后执行
                    return mapData;
                }
            }
            //增加后自定义的触发器
            triggers = e.getInvokeTriggers(EntityInvokeTrigger.WHEN_INVOKE_AT_AFTER_INSERT);
            if(triggers!=null && triggers.size()>0){
                for(EntityInvokeTrigger t : triggers){
                    if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_SQL)){
                        //以sql方式执行
                        tjt.executeByNamedSql(t.getNamedSql(),mapData);
                    }else if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_BEAN)){
                        //以bean方式执行
                        EntityTriggerManager.invokeBeanTriggerMethod(EntityInvokeTrigger.WHEN_INVOKE_AT_AFTER_INSERT, t, user, tjt, mapData, ar);
                        if(ar.hasErrors()){
                            return mapData;
                        }
                    }
                }
            }
            if(e.isTreeTable() && StringUtils.isNotEmpty(e.getTreeTableParentFieldName())){
                //是树表，需要同步更新左右值
                log.debug("这是树表，需要正确计算树表的左右值...");
                //这是树表的增加，先获取增加的节点的父节点下的子节点的最大右值
                String sql = String.format("SELECT MAX(%s) FROM %s WHERE %s=?",
                        e.getTreeTableRightFieldName(),e.getTableName(),e.getTreeTableParentFieldName());
                //获取当个修改实体的父节点值
                String pkId = String.valueOf(mapData.get(e.getTreeTableParentFieldName()));
                int maxRgt = tjt.queryForInt(sql,pkId);
                if(maxRgt==0){
                    //因为指定的新节点下没有任何记录，所以取到的maxRgt就是0
                    log.debug("当前新增节点的父节点之下没有任何子节点，获取父节点的左节点做为新节点的左节点...");
                    //说明此节点下还没有子节点，则获取父节点的左节点做为开始子节点的左值
                    sql = String.format("SELECT %s FROM %s WHERE %s=?",
                            e.getTreeTableLeftFieldName(),e.getTableName(),e.getPrimaryKeys());
                    maxRgt = tjt.queryForInt(sql,pkId);
                }
                if(maxRgt>0){
                    log.debug("计算新增节点的左值为"+maxRgt+",更新所有右值大于"+maxRgt+"的节点的右值+2，同时所有左值大于"+maxRgt+"的节点的左值+2");
                    //处理后续步骤
                    /**
                     * update usr_dept set rgt = rgt +2 where rgt>6;
                     update usr_dept set lft = lft+2 where lft>6;
                     */
                    sql = String.format("UPDATE %s SET %s = %s +2 WHERE %s>?",
                            e.getTableName(),
                            e.getTreeTableRightFieldName(), e.getTreeTableRightFieldName(), e.getTreeTableRightFieldName());
                    tjt.execute(sql, maxRgt);
                    sql = String.format("UPDATE %s SET %s = %s+2 WHERE %s>?",
                            e.getTableName(),
                            e.getTreeTableLeftFieldName(),e.getTreeTableLeftFieldName(),e.getTreeTableLeftFieldName());
                    tjt.execute(sql,maxRgt);
                    log.debug("设置新增节点的左值为"+(maxRgt+1)+",右值为"+(maxRgt+2));
                    mapData.put(e.getTreeTableLeftFieldName().toUpperCase(), maxRgt + 1);
                    mapData.put(e.getTreeTableRightFieldName().toUpperCase(),maxRgt+1+1);
                    sql = String.format("UPDATE %s SET %s=?+1,%s=?+2 WHERE %s=?",
                            e.getTableName(),e.getTreeTableLeftFieldName(),e.getTreeTableRightFieldName(),
                            e.getPrimaryKeys());
                    tjt.execute(sql,maxRgt,maxRgt,mapData.get(e.getPrimaryKeys()));
                }
            }
            tjt.commit();
        }catch (Exception ex){
            tjt.rollback();
            ar.add(String.format("增加实体[%s]失败，异常：%s.",e.getName(),ex.getMessage()));
        }finally {
            tjt.close();
        }
        return mapData;
    }

    @Override
    public Map<String,Object> updateEntity(LoginUser user, Entity e, Map<String, String> parameters, ActionResult ar) {
        if(e.primaryKeys.size()==0){
            throw new ServiceException(String.format("实体[%s]配置有误，没有配置主键，请与开发人员联系！",e.getName()));
        }
        //将参数按照实体属性的类型进行转换
        Map<String,Object> mapData = processParameters(parameters, e, ar);
        if(ar.hasErrors()){
            return mapData;
        }
        //若是树表，同时又是根节点，则不允许被修改
        if(e.isTreeTable()){
            if(StringUtils.isNullOrEmpty(parameters.get(e.getPrimaryKeys()))
                    || mapData.get(e.getPrimaryKeys()).equals(mapData.get(e.getTreeTableParentFieldName()))){
                //父节点为空，或主键与父节点相同
                ar.add("这是根节点，不能被修改！");
                return mapData;
            }
        }
        //将增加人及增加时间放到参数中
        mapData.put(Entity.ADDED_USER_ID_FIELD_NAME,user.getUserId());
        mapData.put(Entity.ADDED_TIME_FIELD_NAME, new Date());
        //数据类型难过，验证必填项
        EntityProperty p;
        Object pValue;
        //先验证是否指定了主键
        for(String key : e.primaryKeys){
            p = e.properties.get(key);
            if(p == null){
                ar.add("err",String.format("实体属性[%s]没有定义，请与开发人员联系！",key));
                return mapData;
            }
            pValue = mapData.get(key);
            //且没有指定值，则提示错误
            if(pValue == null){
                ar.add(p.getFieldName(),String.format("参数[%s(%s)]是主键，请指定主键数据！",
                        p.getLabelName(),key));
            }
        }
        if(ar.hasErrors()){
            return mapData;
        }
        for(String key : e.updatableFields){
            p = e.properties.get(key);
            if(p == null){
                ar.add("err",String.format("实体属性[%s]没有定义，请与开发人员联系！",key));
                return mapData;
            }
            pValue = mapData.get(key);
            //验证必填项
            if(p.isRequired() && p.isUpdatable() && pValue == null){
                //这是必填项，但没有此值
                ar.add(p.getFieldName(),String.format("参数[%s]是必填项，请输入正确的数据！", p.getLabelName()));
            }
        }
        if(ar.hasErrors()){
            return mapData;
        }
        List<Object> args = New.list();
        TransactionalJdbcTemplate tjt = new TransactionalJdbcTemplate(jdbcTemplate.getDataSource(),true);
        try{
            //先根据实体的唯一键来判断是否重复
            List<EntityKey> uniqueKeys = e.getUniqueKeys();
            TwoTuple<String,List<String>> tt;
            if(uniqueKeys!=null && uniqueKeys.size()>0){
                //for update lock
                tjt.queryForInt("SET LOCK_TIMEOUT "+FApplication.SQL_LOCK_TIMEOUT+";SELECT count(*) FROM sys_entity (UPDLOCK) WHERE entity_id=?",e.getEntityId());
                int cnt;
                Object argsValue;
                boolean isNeedToCheck = true;
                for(EntityKey key : uniqueKeys){
                    tt = key.getCheckDoubleForUpdatedSql(e);
                    args.clear();
                    for(String fieldName : tt.second){
                        argsValue = mapData.get(fieldName);
                        if(argsValue==null){
                            isNeedToCheck = false;
                            break;
                        }
                        args.add(argsValue);
                    }
                    if(!isNeedToCheck){
                        continue;
                    }
                    cnt = tjt.queryForInt(tt.first,args.toArray());
                    if(cnt>0){
                        ar.add("dbl",String.format("实体[%s]已存在，判断依据[%s]，请确认！",e.getName(),
                                key.getKeyName()));
                        return mapData;
                    }
                }
            }
            //获取修改前的实体内容，比对，只更新有变化的
            tt = e.getFindByPkSql();
            args.clear();
            for(String key : tt.second){
                args.add(mapData.get(key));
            }
            Map<String,Object> oldData = tjt.queryForMap(tt.first, args.toArray());
            boolean hasTrigger = false;
            //在这里置入修改前触发器
            EntityTrigger trigger = EntityTriggerManager.getTrigger(e.getTriggerClassName());
            if(null != trigger){
                //定义有触发器，调用之
                boolean ret = trigger.beforeUpdate(user,e,mapData,oldData,tjt,ar);
                if(!ret){
                    //触发器返回了false，有再继续执行了
                    return mapData;
                }
                hasTrigger = true;
            }
            //更新前自定义的触发器
            List<EntityInvokeTrigger> triggers = e.getInvokeTriggers(EntityInvokeTrigger.WHEN_INVOKE_AT_BEFORE_UPDATE);
            if(triggers!=null && triggers.size()>0){
                for(EntityInvokeTrigger t : triggers){
                    if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_SQL)){
                        //以sql方式执行
                        tjt.executeByNamedSql(t.getNamedSql(),mapData);
                        hasTrigger = true;
                    }else if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_BEAN)){
                        //以bean方式执行
                        EntityTriggerManager.invokeBeanTriggerMethod(EntityInvokeTrigger.WHEN_INVOKE_AT_BEFORE_UPDATE, t, user, tjt, mapData, ar);
                        if(ar.hasErrors()){
                            return mapData;
                        }
                        hasTrigger = true;
                    }
                }
            }
            //以实体的可更新的字段，比对mapData与oldData，仅更新有变化的数据
            Object newV,oldV;
            args.clear();
            StringBuilder updatedSql = new StringBuilder();
            updatedSql.append("UPDATE ").append(e.getTableName()).append(" SET ");
            List<ChangedData> logChangedList = New.list();
            for(String key : e.updatableFields){
                newV = mapData.get(key);
                oldV = oldData.get(key);
                if(null == newV && oldV == null){
                    continue;
                }
                if(newV != null && oldV != null){
                    //只有当新旧两值都不为空时，才对比
                    if(newV.equals(oldV)){
                        //两值相等
                        continue;
                    }
                }
                if(newV instanceof Boolean){
                    //这是布尔值，将oldV也转换为布尔值后，再比较
                    boolean oldBooleanV = ConvertUtils.toBoolean(String.valueOf(oldV));
                    if(newV == oldBooleanV){
                        continue;
                    }
                }
                //两值不相等，判断是否需要记录修改日志
                p = e.properties.get(key);
                if(p.isLogChanged()){
                    //需要记录日志
                    logChangedList.add(ChangedData.buildChangedData(p.getFieldName(),
                            String.valueOf(oldV),String.valueOf(newV)));
                }
                args.add(newV);
                updatedSql.append(key).append(" = ?,");
            }
            if(args.size()>0){
                //有比对的数据,需要更新
                updatedSql.deleteCharAt(updatedSql.length()-1);
            }
            if(args.size()==0){
                if(!hasTrigger ){
                    //没有修改任何列
                    ar.add("err","您没有修改任何数据，请确认！");
                }
                return mapData;
            }
            updatedSql.append(" WHERE ");
            Map<String,Object> primaryKeyData = New.map();
            for(String key : e.primaryKeys){
                updatedSql.append(key).append(" = ?,");
                args.add(mapData.get(key));
                p = e.properties.get(key);
                primaryKeyData.put(p.getFieldName(), mapData.get(key));
            }
            updatedSql.deleteCharAt(updatedSql.length()-1);
            tjt.execute(updatedSql.toString(),args.toArray());
            if(logChangedList.size()>0){
                //需要记录日志
                logDataChanged(tjt,user,e.getTableName(),primaryKeyData,logChangedList);
            }
            //更新后触发器
            if(null != trigger){
                //定义有触发器，调用之
                trigger.afterUpdate(user, e, mapData, oldData, tjt, ar);
                if(ar.hasErrors()){
                    //触发器中增加了错误消息，不再往后执行
                    return mapData;
                }
            }
            //更新后自定义的触发器
            triggers = e.getInvokeTriggers(EntityInvokeTrigger.WHEN_INVOKE_AT_AFTER_UPDATE);
            if(triggers!=null && triggers.size()>0){
                for(EntityInvokeTrigger t : triggers){
                    if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_SQL)){
                        //以sql方式执行
                        tjt.executeByNamedSql(t.getNamedSql(),mapData);
                    }else if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_BEAN)){
                        //以bean方式执行
                        EntityTriggerManager.invokeBeanTriggerMethod(EntityInvokeTrigger.WHEN_INVOKE_AT_AFTER_UPDATE, t, user, tjt, mapData, ar);
                        if(ar.hasErrors()){
                            return mapData;
                        }
                    }
                }
            }
            if(e.isTreeTable() && StringUtils.isNotEmpty(e.getTreeTableParentFieldName())){
                //树表要求主键必须是一个，若是复合主键，则会出错
                log.debug("这是树表的更新，需要检测是否更新左右值（若父节点与新父节点不一致，则需要更新左右值）");
                //树表
                String newNodePid = String.valueOf(mapData.get(e.getTreeTableParentFieldName())); //新节点的父ID
                //更新前的父ID
                String nodePid = (String)oldData.get(e.getTreeTableParentFieldName());
                if(StringUtils.isNullOrEmpty(nodePid)){
                    log.debug("原父节点为空，这是根节点，不能修改！");
                    throw new RabbitJdbcException("这是根节点，不能修改！");
                }
                List<Map<String,Object>> updatedNodesMap = null;
                if(null!=newNodePid && !newNodePid.equals(nodePid)){
                    log.debug("父节点与新父节点不一致，需要更新左右值");
                    //新节点的父ID与原来的父ID不同，需要更新左右值
                    //获取移动后父节点的当前信息
                    Map<String,Object> newParentNodeMap =  tjt.queryForMap(String.format("select * from %s where %s=?",e.getTableName(),e.getPrimaryKeys()),
                            newNodePid);
                    int mnLft=0,mnRgt=0;
                    mnLft = (Integer)oldData.get("lft");   //这是当前节点的左右值
                    mnRgt = (Integer)oldData.get("rgt");

                    //这是移动后节点的父节点的左右值
                    int newLft=0,newRgt = 0;
                    newLft = (Integer)newParentNodeMap.get("lft");
                    newRgt = (Integer)newParentNodeMap.get("rgt");

                    if(newLft>mnLft && newRgt<mnRgt){
                        log.debug("不能将节点移动到当前节点的子节点下");
                        //这种情况是不允许的，即不能将指定节点移动到此节点的下级节点上
                        throw new RabbitJdbcException("不能将节点移动到当前节点的子节点下！");
                    }
                    //计算待移动的节点的个数，含移动节点本身
                    int movedNodes = (mnRgt-mnLft-1)/2+1;
                    log.debug(String.format("本次需要移动%s个节点，若大于1个节点，则需要先将这些节点临时保存到本地，最后再单独根据步长更新此批节点的左右值",movedNodes));
                    if(movedNodes>1){
                        //待移动的节点有子节点，则先将此批节点保存到临时变量中，方便在更新完左右值时再更新此批待移动的节点的左右值
                        //将移动的节点的所有子节点保存到临时变量中
                        updatedNodesMap = tjt.queryForListMap(
                                String.format("select %s,%s,%s from %s where %s between ? and ?",
                                        e.getPrimaryKeys(), e.getTreeTableLeftFieldName(), e.getTreeTableRightFieldName(),
                                        e.getTableName(), e.getTreeTableLeftFieldName()),
                                mnLft, mnRgt);
                    }
                    //更新左右值，需要分移动节点的右值与新父节点的右值之间的关系，若比父节点的右值小，则需要减，否则需要增加
                    String sql;
                    if(mnRgt<newRgt){
                        log.debug("移动的节点的右值比新父节点的右值小，那么凡移动节点的右值<右值<新父节点的右值，全部减去移动节点数*2；所有移动节点右值<新父节点的左值<=新父节点的右值，左值全部减去移动节点数*2");
                        //先更新右值
                        sql = String.format("UPDATE %s SET %s=%s-? WHERE %s>? AND %s<?",
                                e.getTableName(),e.getTreeTableRightFieldName(),e.getTreeTableRightFieldName(),
                                e.getTreeTableRightFieldName(),e.getTreeTableRightFieldName());
                        tjt.execute(sql, movedNodes * 2, mnRgt, newRgt);
                        //再更新左值，所有左值>当前移动移动的右值并且左值<=新父节点的右值
                        sql = String.format("UPDATE %s SET %s=%s-? WHERE %s>? AND %s<=?",
                                e.getTableName(),e.getTreeTableLeftFieldName(),e.getTreeTableLeftFieldName(),
                                e.getTreeTableLeftFieldName(),e.getTreeTableLeftFieldName());
                        tjt.execute(sql,movedNodes*2,mnRgt,newRgt);
                        //最后，再更移动的节点的左右值
                        sql = String.format("UPDATE %s SET %s=?,%s=? WHERE %s=?",
                                e.getTableName(),e.getTreeTableLeftFieldName(),e.getTreeTableRightFieldName(),
                                e.getPrimaryKeys());
                        if(updatedNodesMap==null){
                            //移动的仅有一个节点，即没有子节点
                            log.debug("本次移动只有一个节点，移动的节点的左值为新父节点的右值-2，右值为新父节点的右值减1");
                            tjt.execute(sql,newRgt-2,newRgt-1,mapData.get(e.getPrimaryKeys()));
                        }else{
                            //移动的是一批节点，需要按照原来的上下级关系依次递减
                            int beginLft = newRgt-movedNodes*2;
                            int step = beginLft-mnLft;
                            log.debug(String.format("本次移动的是一批节点，步长计算方式为：新父节点的右值-移动节点数量*2-移动节点左值，本次的步长为：%s,临时保存的节点的左右值分别增加此步长。",step));
                            List<Object[]> argsList = New.list();
                            Object[] _args;
                            for(Map<String,Object> map : updatedNodesMap){
                                //设置左值参数
                                _args = new Object[3];
                                _args[0] = ((Integer) map.get(e.getTreeTableLeftFieldName()))+step;
                                _args[1] = ((Integer) map.get(e.getTreeTableRightFieldName()))+step;
                                _args[2] = map.get(e.getPrimaryKeys());
                                argsList.add(_args);
                            }
                            tjt.executeBatch(sql,argsList);
                        }
                    }else{
                        //先更新右值，更新所有右值>=新父节点的右值且右值<移动节点的左值,满足这些条件的节点的节值增加移动节点数量*2；
                        log.debug("移动的节点的右值比新父节点的右值大，那么凡新父节点的右值<=右值<移动节点的左值，全部增加移动节点数*2；所有新父节点的右值<新父节点的左值<移动节点左值，左值全部增加移动节点数*2");
                        sql = String.format("UPDATE %s SET %s=%s+? WHERE %s>=? and %s<?",
                                e.getTableName(), e.getTreeTableRightFieldName(), e.getTreeTableRightFieldName(),
                                e.getTreeTableRightFieldName(), e.getTreeTableRightFieldName());
                        tjt.execute(sql, movedNodes * 2, newRgt, mnLft);
                        //再更新左值，所有左值>新父节点的右值且左值<移动节点的右值，满足这些条件的节点的左值增加移动节点数量*2
                        sql = String.format("UPDATE %s SET %s=%s+? WHERE %s>? and %s<?",
                                e.getTableName(), e.getTreeTableLeftFieldName(),e.getTreeTableLeftFieldName(),
                                e.getTreeTableLeftFieldName(),e.getTreeTableLeftFieldName());
                        tjt.execute(sql, movedNodes * 2, newRgt, mnLft);
                        //最后，再更移动的节点的左右值
                        sql = String.format("UPDATE %s SET %s=?,%s=? WHERE %s=?",
                                e.getTableName(), e.getTreeTableLeftFieldName(), e.getTreeTableRightFieldName(),
                                e.getPrimaryKeys());
                        if (updatedNodesMap == null) {
                            //移动的仅有一个节点，即没有子节点
                            //左值为新父节点的右值，右值为新父节点的右值+1
                            log.debug("本次移动只有一个节点，移动的节点的左值为新父节点的右值，右值为新父节点的右值加1");
                            tjt.execute(sql, newRgt, newRgt + 1, mapData.get(e.getPrimaryKeys()));
                        } else {
                            //移动的是一批节点，需要按照原来的上下级关系依次递减
                            int step = mnLft - newRgt;
                            log.debug(String.format("本次移动的是一批节点，步长计算方式为：移动节点的左值-新父节点的右值，本次的步长为：%s,临时保存的节点的左右值分别减去此步长。",step));
                            List<Object[]> argsList = New.list();
                            Object[] _args;
                            for(Map<String,Object> map : updatedNodesMap){
                                //设置左值参数
                                _args = new Object[3];
                                _args[0] = ((Integer) map.get(e.getTreeTableLeftFieldName())) - step;
                                _args[1] = ((Integer) map.get(e.getTreeTableRightFieldName())) - step;
                                _args[2] = map.get(e.getPrimaryKeys());
                                argsList.add(_args);
                            }
                            tjt.executeBatch(sql,argsList);
                        }
                    }
                }else{
                    log.debug("修改节点的父节点与原来的父节点一致，不更新树表左右值！");
                }
            }
            tjt.commit();
        }catch (RabbitJdbcException ex){
            tjt.rollback();
            ar.add(String.format("保存实体[%s]失败，异常：%s.", e.getName(), ex.getMessage()));
        }finally {
            tjt.close();
        }
        return mapData;
    }

    @Override
    public void deleteEntity(LoginUser user, Entity e, Map<String, String> parameters, ActionResult ar) {
        if(e.primaryKeys.size()==0){
            ar.add("err",String.format("实体[%s]配置有误，没有配置主键，请与开发人员联系！",e.getName()));
            return;
        }
        Map<String, String> _params = New.map(e.primaryKeys.size());
        EntityProperty p;
        String value;
        //处理前台传递过来的参数，针对每个参数，去实体配置的主键列表中获取配置，若找到，判断是否des加密，若是，解密之
        for(String key : e.primaryKeys){
            p = e.properties.get(key);
            value = parameters.get(key);
            if(p.isDesEncrypted()){
                value = DES.decrypt(value);
            }
            if(StringUtils.isNullOrEmpty(value)){
                //没有指定主键值
                ar.add("err",String.format("删除实体时必须指定主键[%s(%s)]值！",p.getLabelName(),key));
                return;
            }
            _params.put(key,value);
        }
        List<Object> argsList = New.list();
        TwoTuple<String,List<String>> tt = e.getFindByPkSql();
        for(String key : tt.second){
            argsList.add(_params.get(key));
        }
        Object[] args = argsList.toArray();
        //获取删除实体数据语句，第一个参数为物理删除sql语句，第二个参数为逻辑删除sql
        TwoTuple<String,String> deletedSqlTT = e.getDeletedSql();
        TransactionalJdbcTemplate tjt = new TransactionalJdbcTemplate(jdbcTemplate.getDataSource(),true);
        try{
            Map<String,Object> map = tjt.queryForMap(tt.first,args);
            if(map.size()==0){
                //没有找到实体
                ar.add(String.format("在数据表中没有实体[%s]的任何记录，请确认！",e.getName()));
                return;
            }
            //在删除之前，先检测一下是否是树表，若是树表，则检查所删除的节点下是否子节点，若是，则不允许删除
            if(e.isTreeTable()){
                //看父节点是不是为空，若是，这是第一级节点，在左右值的父级，不允许删除
                if(StringUtils.isNullOrEmpty(MapUtils.getString(map, e.getTreeTableParentFieldName()))){
                    ar.add(String.format("节点[%s]是根节点，不允许删除！",map));
                    return;
                }
                String checkHasSubChildSql = "SELECT COUNT(*) FROM "+e.getTableName()+" WHERE "+e.getTreeTableParentFieldName()+"=?";
                int cnt = tjt.queryForInt(checkHasSubChildSql,map.get(e.getPrimaryKeys()));
                if(cnt > 0){
                    ar.add(String.format("此节点有%s个直属子节点，不允许删除！",cnt));
                    return;
                }
            }
            //删除实体前调用触发器
            EntityTrigger trigger = EntityTriggerManager.getTrigger(e.getTriggerClassName());
            if(null != trigger){
                //定义有触发器，调用之
                boolean ret = trigger.beforeDelete(user, e, _params, map, tjt, ar);
                if(!ret || ar.hasErrors()){
                    //触发器中增加了错误消息，不再往后执行
                    return;
                }
            }
            //删除前自定义的触发器
            List<EntityInvokeTrigger> triggers = e.getInvokeTriggers(EntityInvokeTrigger.WHEN_INVOKE_AT_BEFORE_DELETE);
            if(triggers!=null && triggers.size()>0){
                for(EntityInvokeTrigger t : triggers){
                    if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_SQL)){
                        //以sql方式执行
                        tjt.executeByNamedSql(t.getNamedSql(),map);
                    }else if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_BEAN)){
                        //以bean方式执行
                        EntityTriggerManager.invokeBeanTriggerMethod(EntityInvokeTrigger.WHEN_INVOKE_AT_BEFORE_DELETE, t, user, tjt, map, ar);
                        if(ar.hasErrors()){
                            return;
                        }
                    }
                }
            }
            //删除实体
            boolean physicalDeleted = true;
            if(StringUtils.isNotEmpty(e.getDeletedFieldName())){
                //配置此实体支持逻辑删除
                //在此情况下，加try catch物理删除之，若报错，则逻辑删除
                try{
                    tjt.execute(deletedSqlTT.first,args);
                }catch (RabbitJdbcException jdbcException){
                    //物理删除失败，肯定有其它表引用此实体数据，则改为逻辑删除
                    physicalDeleted = false;
                    tjt.execute(deletedSqlTT.second,args);
                }
            }else{
                //不支持逻辑删除，直接物理删除
                tjt.execute(deletedSqlTT.first,args);
            }
            //删除后调用触发器
            if(null != trigger){
                //定义有触发器，调用之
                trigger.afterDelete(user, e, _params, map, tjt, ar);
                if(ar.hasErrors()){
                    //触发器中增加了错误消息，不再往后执行
                    return;
                }
            }
            //删除后自定义的触发器
            triggers = e.getInvokeTriggers(EntityInvokeTrigger.WHEN_INVOKE_AT_AFTER_DELETE);
            if(triggers!=null && triggers.size()>0){
                for(EntityInvokeTrigger t : triggers){
                    if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_SQL)){
                        //以sql方式执行
                        tjt.executeByNamedSql(t.getNamedSql(),map);
                    }else if(t.getExecuteTypeId().equals(EntityInvokeTrigger.EXECUTE_TYPE_BEAN)){
                        //以bean方式执行
                        EntityTriggerManager.invokeBeanTriggerMethod(EntityInvokeTrigger.WHEN_INVOKE_AT_AFTER_DELETE, t, user, tjt, map, ar);
                        if(ar.hasErrors()){
                            return;
                        }
                    }
                }
            }
            if(e.isTreeTable() && physicalDeleted && StringUtils.isNotEmpty(e.getTreeTableParentFieldName())){
                //这是树表
                log.debug(String.format("%s是树表，需要正确处理左右值。",e.getTableName()));
                if (map.size()>0) {
                    int lft = ConvertUtils.toInt(map.get(e.getTreeTableLeftFieldName()),0);
                    int rgt = ConvertUtils.toInt(map.get(e.getTreeTableRightFieldName()), 0);
                    if(rgt-lft>0){
                        log.debug(String.format("获取到删除的节点的左值为%s,右值为%s，在删除的同时并更新所有大于左值的节点的左值-2，大于右值的节点的右值-2",
                                lft,rgt));
                        //update usr_dept set lft=lft-2 where lft>7;
                        //update usr_dept set rgt=rgt-2 where rgt>8;
                        String _sql = String.format("UPDATE %s SET %s=%s-2 WHERE %s>?",
                                e.getTableName(),e.getTreeTableLeftFieldName(),e.getTreeTableLeftFieldName(),
                                e.getTreeTableLeftFieldName());
                        tjt.execute(_sql,lft);
                        _sql = String.format("update %s set %s=%s-2 where %s>?",
                                e.getTableName(),e.getTreeTableRightFieldName(),e.getTreeTableRightFieldName(),
                                e.getTreeTableRightFieldName());
                        tjt.execute(_sql, rgt);
                    }
                } else {
                    throw new SQLException("参数为【" + parameters.get(e.getPrimaryKeys()) + "】的记录没找到！");
                }
            }
            tjt.commit();
            log.info(String.format("删除实体[%s]：%s",e.getName(),map));
        }catch (Exception ex){
            tjt.rollback();
            ar.add(String.format("删除实体[%s]失败，异常：%s",e.getName(),ex.getMessage()));
        }finally {
            tjt.close();
        }
    }

    @Override
    public String getRichContentById(String richTextId) {
        return (String)jdbcTemplate.queryForObject("SELECT rich_content FROM sys_rich_text WHERE rich_text_id=?",richTextId);
    }

    @Override
    public Map<String,Object> getRichContent(ServiceContext serviceContext) {
        String bt = serviceContext.getDesId("bt");
        String btPk = serviceContext.getDesId("bt_pk");
        String btPkId = serviceContext.getDesId("bt_pk_id");
        if(StringUtils.isNullOrEmpty(btPkId)){
            btPkId = serviceContext.params.getString("bt_pk_id");
        }
        if(StringUtils.isNullOrEmpty(bt)
                || StringUtils.isNullOrEmpty(btPk)
                || StringUtils.isNullOrEmpty(btPkId)){
            throw new ServiceException("参数错误，请同时指定[bt、bt_pk、bt_pk_id]三个参数！");
        }
        String mid = serviceContext.params.getString("mid");
        if(StringUtils.isNullOrEmpty(mid)){
            throw new ServiceException("参数错误，请指定模块ID[mid]参数！");
        }
        String richTextId = (String)serviceContext.getJdbcTemplate().queryForObject(String.format("select rich_text_id from %s where %s=?",bt,btPk),
                btPkId);
        Map<String,Object> map = New.map();
        map.put("bt",serviceContext.params.get("bt"));
        map.put("bt_pk",serviceContext.params.get("bt_pk"));
        map.put("bt_pk_id",serviceContext.params.get("bt_pk_id"));
        map.put("userId",serviceContext.getUser().getUserId());
        map.put("mid",mid);
        if(StringUtils.isNotEmpty(richTextId)){
            //这是修改
            map.put("content",getRichContentById(richTextId));
            map.put("rich_text_id", DES.encrypt(richTextId));
        }
        return map;
    }

    @Override
    public String saveRichContent(ServiceContext serviceContext) {
        String richTextId = serviceContext.getDesId("rich_text_id");
        String content = serviceContext.params.getString("editorValue");
        if(StringUtils.isNullOrEmpty(content)) {
            throw new ServiceException("参数错误，请指定[content]参数！");
        }
        if(StringUtils.isNullOrEmpty(richTextId)){
            //这是增加
            richTextId = PG.getNext("RCH");
            //需要指定业务表名称及业务表的主键字段名及业务表的主键值三个参数
            String bt = serviceContext.getDesId("bt");
            String btPk = serviceContext.getDesId("bt_pk");
            String btPkId = serviceContext.getDesId("bt_pk_id");
            if(StringUtils.isNullOrEmpty(btPkId)){
                btPkId = serviceContext.params.getString("bt_pk_id");
            }
            if(StringUtils.isNullOrEmpty(bt)
                    || StringUtils.isNullOrEmpty(btPk)
                    || StringUtils.isNullOrEmpty(btPkId)){
                throw new ServiceException("参数错误，请同时指定[bt、bt_pk、bt_pk_id]三个参数！");
            }
            serviceContext.getJdbcTemplate().execute("INSERT INTO sys_rich_text(rich_text_id,rich_content,added_user_id,added_time)VALUES(?,?,?,?)",
                    richTextId,content,serviceContext.getUser().getUserId(),new Date());
            //将主键写回到业务表中
            serviceContext.getJdbcTemplate().execute(String.format("UPDATE %s SET rich_text_id=? WHERE %s=?",bt,btPk),
                    richTextId,btPkId);
        }else{
            //修改富文本内容
            serviceContext.getJdbcTemplate().execute("UPDATE sys_rich_text SET rich_content=? WHERE rich_text_id=?",
                    content,richTextId);
        }
        return richTextId;
    }


    @Override
    public List<Map<String, Object>> getYears(ServiceContext serviceContext) {
        List<Map<String, Object>> list = New.list(7);
        int year = DateUtils.getYear(new Date());
        Map<String,Object> map;
        for(int i=year-5;i<=year+1;i++){
            map = New.map(2);
            map.put("id",i);
            map.put("text",String.format("%s年",i));
            list.add(map);
        }
        return list;
    }

    public static final RowMapper<WebRequestMetadata> requestRowMapper = new RowMapper<WebRequestMetadata>() {
        @Override
        public WebRequestMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
            WebRequestMetadata r = new WebRequestMetadata();
            r.setWebRequestId(rs.getString("web_request_id"));
            r.setFunctionId(rs.getString("function_id"));
            r.setName(rs.getString("name"));
            r.setCheckFunctionPrivilege(rs.getBoolean("check_function_privilege"));
            r.setActionId(rs.getString("action_id"));
            r.setCheckActionPrivilege(rs.getBoolean("check_action_privilege"));
            r.setRequestType(rs.getString("request_type"));
            r.setResponseType(rs.getString("response_type"));
            r.setForwardUrl(rs.getString("url"));
            r.setBeanClassName(rs.getString("bean_class_name"));
            r.setBeanClassMethodName(rs.getString("bean_class_method_name"));
            r.setLogSupported(rs.getBoolean("is_log_supported"));
            r.setDataSourceKey(rs.getString("data_source_key"));
            r.setJdbcTemplateType(rs.getString("jdbc_template_type"));
            r.setTransactionLevel(rs.getInt("transaction_level"));
            return r;
        }
    };

    public static RowMapper<QueryMetadata> queryMetadataRowMapper = new RowMapper<QueryMetadata>() {
        @Override
        public QueryMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
            QueryMetadata qmd = new QueryMetadata();
            qmd.setQueryId(rs.getString("query_id"));
            qmd.setCheckFunctionPrivilege(rs.getBoolean("is_check_function_privilege"));
            qmd.setFunctionId(rs.getString("function_id"));
            qmd.setQueryKey(rs.getString("query_key"));
            qmd.setName(rs.getString("name"));
            qmd.setQuerySql(rs.getString("query_sql"));
            qmd.setNamedSql(rs.getBoolean("is_named_sql"));
            qmd.setProcedure(rs.getBoolean("is_procedure"));
            qmd.setActionsColumnWidth(JdbcUtils.getInteger(rs, "actions_column_width"));
            qmd.setBeanClassName(rs.getString("bean_class_name"));
            qmd.setBuildWhereMethodName(rs.getString("build_where_method_name"));
            qmd.setPagingSupported(rs.getBoolean("is_paging_supported"));
            qmd.setPageSize(rs.getInt("page_size"));
            qmd.setPageList(rs.getString("page_size_list"));
            qmd.setFooterSupported(rs.getBoolean("is_footer_supported"));
            qmd.setMultiColumnSortedSupported(rs.getBoolean("is_multi_column_sorted_supported"));
            qmd.setSingleSelect(rs.getBoolean("is_single_select"));
            qmd.setTreeTable(rs.getBoolean("is_tree"));
            qmd.setShowRowNumber(rs.getBoolean("is_show_row_number"));
            qmd.setFrozenLeftColumns(rs.getInt("frozen_left_columns"));
            qmd.setFrozenTopRows(rs.getInt("frozen_top_rows"));
            return qmd;
        }
    };
    public static RowMapper<QueryFieldMetadata> queryFieldMetadataRowMapper = new RowMapper<QueryFieldMetadata>() {
        @Override
        public QueryFieldMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
            QueryFieldMetadata f = new QueryFieldMetadata();
            f.setQueryFieldId(rs.getString("query_field_id"));
            f.setQueryId(rs.getString("query_id"));
            f.setTableAliasName(rs.getString("table_alias_name"));
            f.setFieldName(rs.getString("field_name"));
            f.setUsedFieldNameAsKey(rs.getBoolean("is_used_field_name_as_key"));
            f.setDisplayName(rs.getString("display_name"));
            f.setDisplayNameTag(rs.getString("display_name_tag"));
            f.setWidth(rs.getInt("width"));
            f.setDataType(DataType.parseDataType(rs.getString("data_type")));
            f.setPrimaryKey(rs.getBoolean("is_primary_key"));
            f.setSortSupported(rs.getBoolean("is_sort_supported"));
            f.setAsDefaultSort(rs.getBoolean("as_default_sort"));
            f.setDefaultSortOrder(rs.getString("default_sort_order"));
            f.setColumnHeaderAlign(DataAlign.parseDataAlign(rs.getString("column_header_align")));
            f.setColumnDataAlign(DataAlign.parseDataAlign(rs.getString("column_data_align")));
            f.setSuppressible(rs.getBoolean("is_suppressible"));
            f.setAsTreeField(rs.getBoolean("as_tree_field"));
            f.setAsTreeParentField(rs.getBoolean("as_tree_parent_field"));
            f.setFormatType(FormatType.parseFormatType(rs.getString("format_type")));
            f.setFormatOptions(rs.getString("format_options"));
            f.setDisplayOrder(JdbcUtils.getFloatValue(rs, "display_order"));
            return f;
        }
    };

    //实体
    public static final RowMapper<Entity> entityRowMapper = new RowMapper<Entity>() {
        @Override
        public Entity mapRow(ResultSet rs, int rowNum) throws SQLException {
            Entity e = new Entity();
            e.setEntityId(rs.getString("entity_id"));
            e.setName(rs.getString("name"));
            e.setTableName(rs.getString("table_name"));
            e.setTreeTable(rs.getBoolean("is_tree"));
            e.setTag(rs.getString("tag"));
            //借用functionId为实体触发器
            e.setTriggerClassName(rs.getString("trigger_class_name"));
            if(e.isTreeTable()){
                e.setTreeTableParentFieldName(rs.getString("tree_parent_field_name"));
                e.setTreeTableLeftFieldName(rs.getString("tree_lft_field_name"));
                e.setTreeTableRightFieldName(rs.getString("tree_rgt_field_name"));
            }
            //权限相关的几个属性
            e.setFunctionId(rs.getString("function_id"));
            //e.setCheckFunctionPrivilege(rs.getBoolean("is_check_function_privilege"));
            e.setActionId(rs.getString("action_id"));
            e.setInsertActionId(rs.getString("insert_action_id"));
            e.setUpdateActionId(rs.getString("update_action_id"));
            e.setDeleteActionId(rs.getString("delete_action_id"));
            return e;
        }
    };

    //实体属性
    public static final RowMapper<EntityProperty> entityPropertyRowMapper = new RowMapper<EntityProperty>() {
        @Override
        public EntityProperty mapRow(ResultSet rs, int rowNum) throws SQLException {
            EntityProperty p = new EntityProperty();
            p.setEntityId(rs.getString("entity_id"));
            p.setFieldName(rs.getString("table_field_name"));
            p.setLabelName(rs.getString("property_name"));
            p.setDesEncrypted(rs.getBoolean("is_des_encrypt"));
            p.setLogChanged(rs.getBoolean("log_changed"));
            p.setDataType(DataType.parseDataType(rs.getString("data_type")));
            p.setRequired(!rs.getBoolean("is_nullable"));
            //设置默认值，借助于dataInputOptions字段
            p.setDataInputOptions(rs.getString("default_value"));
            if(StringUtils.isNotEmpty(p.getDataInputOptions())){
                //设有默认值
                try {
                    p.setDefaultValue(j.framework.util.ConvertUtils.convertToCorrectData(p.getDataInputOptions(), p.getDataType()));
                }catch (Exception ignored){
                    p.setDefaultValue(null);
                }
            }
            p.setMinValue(rs.getString("min_value"));
            p.setMaxValue(rs.getString("max_value"));
            p.setDataInputType(rs.getString("data_input_type"));
            p.setDataInputOptions(rs.getString("data_input_options"));
            p.setSequenceKey(rs.getString("sequence_key"));
            p.setUpdatable(rs.getBoolean("updatable"));
            p.setInsertable(rs.getBoolean("insertable"));
            p.setDeletedFieldTag(rs.getBoolean("is_deleted_field"));
            return p;
        }
    };

    private void loadEntityProperty(Entity e,EntityProperty p){
        e.properties.put(p.getFieldName(),p);
        //检测此字段是否定义有序列，若有，将此序列增加到表中
        if(StringUtils.isNotEmpty(p.getSequenceKey())){
            e.setSequenceKey(p.getSequenceKey());
            p.setSequenceKey(null);
        }
        //此字段是否可插入
        if(p.isInsertable()){
            e.insertableFields.add(p.getFieldName());
        }
        //此字段是否可修改
        if(p.isUpdatable()){
            e.updatableFields.add(p.getFieldName());
        }
        //此字段是否逻辑删除标记
        if(p.isDeletedFieldTag()){
            e.setDeletedFieldName(p.getFieldName());
        }
        //处理最小值与最大值
        if(StringUtils.isNotEmpty(p.getMinValue())){
            if(p.getDataType().equals(DataType.INTEGER)){
                p.setMinIntValue(j.framework.util.ConvertUtils.toInteger(p.getMinValue()));
            }else if(p.getDataType().equals(DataType.FLOAT)){
                p.setMinFloatValue(ConvertUtils.toSingle(p.getMinValue()));
            }else if(p.getDataType().equals(DataType.DOUBLE)){
                p.setMinDoubleValue(ConvertUtils.toDouble(p.getMinValue()));
            }else if(p.getDataType().equals(DataType.DATE)){
                p.setMinDateValue(DateUtils.toDate(p.getMinValue(), "yyyy-MM-dd"));
            }else if(p.getDataType().equals(DataType.DATETIME)){
                p.setMinDateValue(DateUtils.toDate(p.getMinValue(), "yyyy-MM-dd HH:mm:ss"));
            }
        }
        if(StringUtils.isNotEmpty(p.getMaxValue())){
            if(p.getDataType().equals(DataType.INTEGER)){
                p.setMaxIntValue(j.framework.util.ConvertUtils.toInteger(p.getMaxValue()));
            }else if(p.getDataType().equals(DataType.FLOAT)){
                p.setMaxFloatValue(ConvertUtils.toSingle(p.getMaxValue()));
            }else if(p.getDataType().equals(DataType.DOUBLE)){
                p.setMaxDoubleValue(ConvertUtils.toDouble(p.getMaxValue()));
            }else if(p.getDataType().equals(DataType.DATE)){
                p.setMaxDateValue(DateUtils.toDate(p.getMaxValue(), "yyyy-MM-dd"));
            }else if(p.getDataType().equals(DataType.DATETIME)){
                p.setMaxDateValue(DateUtils.toDate(p.getMaxValue(), "yyyy-MM-dd HH:mm:ss"));
            }
        }
        p.setEntityId(null);
    }
    /**
     * 处理用户传过来的参数，根据实体配置的数据类型，转换每一个参数
     * @param parameters
     * @param e
     * @param ar
     * @return
     */
    private static Map<String,Object> processParameters(Map<String,String> parameters,Entity e,ActionResult ar){
        Map<String,Object> mapData = New.map();
        EntityProperty p;
        String pValue,tmp;
        Integer intValue;
        Float floatValue;
        Double doubleValue;
        Long longValue;
        Date dateValue;
        Short shortValue;
        //将所有带下划线的的key放到目标数组中
        for(Map.Entry<String,String> item:parameters.entrySet()){
            if(item.getKey().startsWith("_")){
                mapData.put(item.getKey(), item.getValue());
            }
        }
        for(Map.Entry<String,EntityProperty> entry : e.properties.entrySet()){
            pValue = parameters.get(entry.getKey());
            p = entry.getValue();
            if(pValue!=null && StringUtils.isNotEmpty(pValue)){
                if(entry.getValue().getDataType().equals(DataType.STRING)){
                    tmp = pValue;
                    if(entry.getValue().isDesEncrypted()){
                        //这是主键，解密
                        tmp = DES.decrypt(pValue);
                        if(StringUtils.isNullOrEmpty(tmp)){
                            tmp = pValue;
                        }
                    }
                    if(StringUtils.isNotEmpty(p.getDataInputType())) {
                        if (p.getDataInputType().equals(EntityProperty.DATA_INPUT_TYPE_PHONE)) {
                            ar.isValidTelephone(p.getFieldName(),
                                    tmp, String.format("参数[%s]格式输入有误，请输入正确的电话类型！", p.getLabelName()));
                        } else if (p.getDataInputType().equals(EntityProperty.DATA_INPUT_TYPE_MOBILE)) {
                            ar.isValidMobile(p.getFieldName(),
                                    tmp, String.format("参数[%s]格式输入有误，请输入正确的移动号码类型！", p.getLabelName()));
                        } else if (p.getDataInputType().equals(EntityProperty.DATA_INPUT_TYPE_EMAIL)) {
                            ar.isValidEmail(p.getFieldName(),
                                    tmp, String.format("参数[%s]格式输入有误，请输入正确的电子邮件类型！", p.getLabelName()));
                        } else if (p.getDataInputType().equals(EntityProperty.DATA_INPUT_TYPE_IDCARD)) {
                            ar.isValidIdentityCard(p.getFieldName(),
                                    tmp, String.format("参数[%s]格式输入有误，请输入正确的身份证号！", p.getLabelName()));
                        } else if (p.getDataInputType().equals(EntityProperty.DATA_INPUT_TYPE_REGEX)
                                && StringUtils.isNotEmpty(p.getDataInputOptions())) {
                            //检测用户所输入的正则表达式有无错误
                            if (!tmp.matches(p.getDataInputOptions())) {
                                ar.add(p.getFieldName(),
                                        String.format("参数[%s]格式输入有误，请按正则表达式[%s]格式输入！", p.getLabelName(),
                                                p.getDataInputOptions()));
                            }

                        } else if (p.getDataInputType().equals(EntityProperty.DATA_INPUT_TYPE_URL)) {
                            ar.isValidURL(p.getFieldName(),
                                    tmp, String.format("参数[%s]格式输入有误，请输入正确的网址格式！", p.getLabelName()));
                        }
                    }
                    //若字符串类型的为空白字符串，则统一设为NULL
                    if(StringUtils.isNullOrEmpty(tmp)){
                        tmp = null;
                    }
                    mapData.put(entry.getKey(),tmp);
                }else if(p.getDataType().equals(DataType.INTEGER)){
                    try {
                        intValue = j.framework.util.ConvertUtils.toInteger(pValue);
                        if(intValue!=null) {
                            if (p.getMinIntValue() != null && p.getMaxIntValue() != null) {
                                if (intValue < p.getMinIntValue() || intValue > p.getMaxIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须在%s-%s之间！",
                                            p.getLabelName(), p.getMinIntValue(), p.getMaxIntValue()));
                                }
                            } else if (p.getMinIntValue() != null) {
                                if (intValue < p.getMinIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须大于等于%s！",
                                            p.getLabelName(), p.getMinIntValue()));
                                }
                            } else if (p.getMaxIntValue() != null) {
                                if (intValue > p.getMaxIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须小于等于%s！",
                                            p.getLabelName(), p.getMaxIntValue()));
                                }
                            }
                        }
                        mapData.put(entry.getKey(), intValue);
                    }catch (Exception ex){
                        ar.add(p.getFieldName(),String.format("参数[%s]输入有误，请输入正确的数字类型!",p.getLabelName()));
                    }
                }else if(p.getDataType().equals(DataType.FLOAT)){
                    try {
                        floatValue = ConvertUtils.toSingle(pValue);
                        if(floatValue!=null) {
                            if (p.getMinFloatValue() != null && p.getMaxFloatValue() != null) {
                                if (floatValue < p.getMinFloatValue() || floatValue > p.getMaxFloatValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须在%s-%s之间！",
                                            p.getLabelName(), p.getMinFloatValue(), p.getMaxFloatValue()));
                                }
                            } else if (p.getMinFloatValue() != null) {
                                if (floatValue < p.getMinFloatValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须大于等于%s！",
                                            p.getLabelName(), p.getMinFloatValue()));
                                }
                            } else if (p.getMaxFloatValue() != null) {
                                if (floatValue > p.getMaxFloatValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须小于等于%s！",
                                            p.getLabelName(), p.getMaxFloatValue()));
                                }
                            }
                        }
                        mapData.put(entry.getKey(), floatValue);
                    }catch (Exception ex){
                        ar.add(p.getFieldName(),String.format("参数[%s]输入有误，请输入正确的单精度类型!",p.getLabelName()));
                    }
                }else if(p.getDataType().equals(DataType.DOUBLE)){
                    try {
                        doubleValue = ConvertUtils.toDouble(pValue);
                        if(doubleValue!=null) {
                            if (p.getMinDoubleValue() != null && p.getMaxDoubleValue() != null) {
                                if (doubleValue < p.getMinDoubleValue() || doubleValue > p.getMaxDoubleValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须在%s-%s之间！",
                                            p.getLabelName(), p.getMinDoubleValue(), p.getMaxDoubleValue()));
                                }
                            } else if (p.getMinDoubleValue() != null) {
                                if (doubleValue < p.getMinDoubleValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须大于等于%s！",
                                            p.getLabelName(), p.getMinDoubleValue()));
                                }
                            } else if (p.getMaxDoubleValue() != null) {
                                if (doubleValue > p.getMaxDoubleValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须小于等于%s！",
                                            p.getLabelName(), p.getMaxDoubleValue()));
                                }
                            }
                        }
                        mapData.put(entry.getKey(), doubleValue);
                    }catch (Exception ex){
                        ar.add(p.getFieldName(),String.format("参数[%s]输入有误，请输入正确的双精度类型!",p.getLabelName()));
                    }
                }else if(p.getDataType().equals(DataType.DATE)){
                    try {
                        dateValue = DateUtils.toDate(pValue, "yyyy-MM-dd");
                        if(dateValue!=null) {
                            if (p.getMinDateValue() != null && p.getMaxDoubleValue() != null) {
                                if (dateValue.before(p.getMinDateValue()) || dateValue.after(p.getMaxDateValue())) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须在%s-%s之间！",
                                            p.getLabelName(), p.getMinDateValue(), p.getMaxDateValue()));
                                }
                            } else if (p.getMinDateValue() != null) {
                                if (dateValue.before(p.getMinDateValue())) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须晚于或等于%s！",
                                            p.getLabelName(), p.getMinDateValue()));
                                }
                            } else if (p.getMaxDateValue() != null) {
                                if (dateValue.after(p.getMaxDateValue())) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须早于或等于%s！",
                                            p.getLabelName(), p.getMaxDateValue()));
                                }
                            }
                        }
                        mapData.put(entry.getKey(), dateValue);
                    }catch (Exception ex){
                        ar.add(p.getFieldName(),String.format("参数[%s]输入有误，请输入正确的日期类型!",p.getLabelName()));
                    }
                }else if(p.getDataType().equals(DataType.DATETIME)){
                    try {
                        String dateFormat = "yyyy-MM-dd";
                        int timeSplitCnt = StringUtils.countOccurrencesOf(pValue, ":");
                        if(timeSplitCnt == 1){
                            dateFormat += " HH:mm";
                        }else if(timeSplitCnt == 2){
                            dateFormat += " HH:mm:ss";
                        }
                        dateValue = DateUtils.toDate(pValue, dateFormat);
                        if(dateValue!=null) {
                            if (p.getMinDateValue() != null && p.getMaxDoubleValue() != null) {
                                if (dateValue.before(p.getMinDateValue()) || dateValue.after(p.getMaxDateValue())) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须在%s-%s之间！",
                                            p.getLabelName(), p.getMinDateValue(), p.getMaxDateValue()));
                                }
                            } else if (p.getMinDateValue() != null) {
                                if (dateValue.before(p.getMinDateValue())) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须晚于或等于%s！",
                                            p.getLabelName(), p.getMinDateValue()));
                                }
                            } else if (p.getMaxDateValue() != null) {
                                if (dateValue.after(p.getMaxDateValue())) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须早于或等于%s！",
                                            p.getLabelName(), p.getMaxDateValue()));
                                }
                            }
                        }
                        mapData.put(entry.getKey(), dateValue);
                    }catch (Exception ex){
                        ar.add(p.getFieldName(),String.format("参数[%s]输入有误，请输入正确的日期时间类型!",p.getLabelName()));
                    }
                }else if(p.getDataType().equals(DataType.LONG)){
                    try {
                        longValue = ConvertUtils.toLong(pValue);
                        if(longValue!=null) {
                            if (p.getMinIntValue() != null && p.getMaxIntValue() != null) {
                                if (longValue < p.getMinIntValue() || longValue > p.getMaxIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须在%s-%s之间！",
                                            p.getLabelName(), p.getMinIntValue(), p.getMaxIntValue()));
                                }
                            } else if (p.getMinIntValue() != null) {
                                if (longValue < p.getMinIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须大于等于%s！",
                                            p.getLabelName(), p.getMinIntValue()));
                                }
                            } else if (p.getMaxIntValue() != null) {
                                if (longValue > p.getMaxIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须小于等于%s！",
                                            p.getLabelName(), p.getMaxIntValue()));
                                }
                            }
                        }
                        mapData.put(entry.getKey(), longValue);
                    }catch (Exception ex){
                        ar.add(p.getFieldName(),String.format("参数[%s]输入有误，请输入正确的长整型!",p.getLabelName()));
                    }
                }else if(p.getDataType().equals(DataType.SHORT)){
                    try {
                        shortValue = j.framework.util.ConvertUtils.toShort(pValue);
                        if(shortValue!=null) {
                            if (p.getMinIntValue() != null && p.getMaxIntValue() != null) {
                                if (shortValue < p.getMinIntValue() || shortValue > p.getMaxIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须在%s-%s之间！",
                                            p.getLabelName(), p.getMinIntValue(), p.getMaxIntValue()));
                                }
                            } else if (p.getMinIntValue() != null) {
                                if (shortValue < p.getMinIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须大于等于%s！",
                                            p.getLabelName(), p.getMinIntValue()));
                                }
                            } else if (p.getMaxIntValue() != null) {
                                if (shortValue > p.getMaxIntValue()) {
                                    ar.add(p.getFieldName(), String.format("参数[%s]的值必须小于等于%s！",
                                            p.getLabelName(), p.getMaxIntValue()));
                                }
                            }
                        }
                        mapData.put(entry.getKey(), shortValue);
                    }catch (Exception ex){
                        ar.add(p.getFieldName(),String.format("参数[%s]输入有误，请输入正确的短整型[%s-%s]!",
                                p.getLabelName(),
                                Short.MIN_VALUE,Short.MAX_VALUE));
                    }
                }else if(p.getDataType().equals(DataType.BOOLEAN)){
                    mapData.put(entry.getKey(), ConvertUtils.toBoolean(pValue));
                }
            }else{
                if(p.getDataType().equals(DataType.BOOLEAN)){
                    mapData.put(entry.getKey(),false);
                }else{
                    //检查是否有默认值，若有，则设置默认值
                    if(p.getDefaultValue()!=null){
                        mapData.put(entry.getKey(),p.getDefaultValue());
                    }else{
                        mapData.put(entry.getKey(),null);
                    }
                }
            }
        }
        return mapData;
    }
    /**
     * 记录业务表的修改日志,将业务表的修改日志记录到统一地方
     * @param jdbcTemplate 数据库连接
     * @param user 当前操作人
     * @param tableName 业务表的名称
     * @param primaryKeyData 主键数据，支持复合主键
     * @param logChangedList 修改的值明细
     */
    public static void logDataChanged(JdbcTemplate jdbcTemplate, LoginUser user, String tableName, Map<String, Object> primaryKeyData, List<ChangedData> logChangedList) {
        String pkId = "";
        StringBuilder json = new StringBuilder("{");
        int index = 0;
        for(Map.Entry<String,Object> entry : primaryKeyData.entrySet()){
            if(index++==0){
                //这是第一个
                pkId = (String)entry.getValue();
            }
            json.append("\"").append(entry.getKey()).append("\":\"").append(entry.getValue()).append("\"");
        }
        json.append("}");
        logDataChanged(jdbcTemplate, user, tableName, pkId, json.toString(), logChangedList);
    }

    /**
     * 记录业务表的修改日志
     * @param jdbcTemplate 数据库连接
     * @param user 当前操作人
     * @param tableName 业务表的名称
     * @param pkId 主键，不支持复合主键，若是复合主键，请将复合主键组合成json放到summary字段中
     * @param summary 修改摘要
     * @param logChangedList 修改的值明细
     */
    public static void logDataChanged(JdbcTemplate jdbcTemplate, LoginUser user,
                                      String tableName, String pkId,String summary, List<ChangedData> logChangedList){
        String historyId = PG.getNext("KJ8");
        jdbcTemplate.execute("INSERT INTO sys_table_update_history(update_history_id,table_name,table_pk_id,summary,added_user_id,added_time)values(?,?,?,?,?,?)",
                historyId,tableName,pkId,summary,user.getUserId(),new Date());
        if(logChangedList!=null && logChangedList.size()>0) {
            List<Object[]> argsList = New.list();
            Object[] args;
            for (ChangedData data : logChangedList) {
                args = new Object[4];
                args[0] = historyId;
                args[1] = data.getFieldName();
                args[2] = data.getOldValue();
                args[3] = data.getNewValue();
                argsList.add(args);
            }
            jdbcTemplate.executeBatch("INSERT INTO sys_table_update_history_details(update_history_id,field_name,old_value,new_value)VALUES(?,?,?,?)", argsList);
        }
    }

    private static RowMapper<EntityInvokeTrigger> entityInvokeTriggerRowMaper = new RowMapper<EntityInvokeTrigger>() {
        @Override
        public EntityInvokeTrigger mapRow(ResultSet rs, int rowNum) throws SQLException {
            EntityInvokeTrigger t = new EntityInvokeTrigger();
            t.setTriggerName(rs.getString("trigger_name"));
            t.setWhenTriggerInvoke(rs.getInt("when_trigger_invoke"));
            t.setExecuteTypeId(rs.getString("execute_type_id"));
            t.setNamedSql(rs.getString("named_sql"));
            t.setBeanClassName(rs.getString("bean_class_name"));
            t.setBeanMethodName(rs.getString("bean_method_name"));
            return t;
        }
    };
}
